// Copyright 2023 The Lynx Authors. All rights reserved.
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.

#ifndef CORE_RUNTIME_JSCACHE_QUICKJS_BYTECODE_QUICKJS_BYTECODE_PROVIDER_H_
#define CORE_RUNTIME_JSCACHE_QUICKJS_BYTECODE_QUICKJS_BYTECODE_PROVIDER_H_

#include <memory>
#include <string>
#include <utility>

#include "base/include/expected.h"
#include "core/runtime/jscache/quickjs/bytecode/quickjs_bytecode.h"
#include "core/runtime/jscache/quickjs/bytecode/quickjs_bytecode_provider_src.h"
#include "core/runtime/jsi/jsi.h"
#include "core/template_bundle/template_codec/version.h"

namespace lynx {
namespace piper {
namespace quickjs {
// `QuickjsBytecodeProvider` provides an interface for encoding and
// decoding JS binary bytecode buffers. Compared with bytecode encoding and
// decoding directly through the JS engine, the binary generated by it will
// carry additional information and provide other capabilities.
// It can be constructed from source code and compiled to generate packed JS
// bytecode; or can be constructed from packed bytecode to obtain raw
// bytecode and various packed information from it.
//
// A way to compile from js source code using `QuickjsBytecodeProvider`:
//
//   auto source = QuickjsBytecodeProvider::FromSource(
//       "url", std::make_shared<StringBuffer>("var v = 0;"));
//   auto provider = source.Compile(base::Version("2.10"), {});
//   if (provider) {
//     return provider->GetPackedBytecodeBuffer();
//   }
//
// A way to get raw bytecode from packed buffer:
//
//   auto bytecode = QuickjsBytecodeProvider::FromPackedBytecode(buffer);
//   if (bytecode.first) {
//     auto target_sdk_version = bytecode.first->GetTargetSdkVersion();
//     auto raw_bytecode = bytecode.first->GetRawBytecode();
//   }
class QuickjsBytecodeProvider final {
 public:
  /**
   * Check if the given buffer is a bytecode buffer packed by
   * `QuickjsBytecodeProvider`.
   * @param buffer buffer to check.
   * @return true if the given buffer is a bytecode buffer packed by
   * `QuickjsBytecodeProvider`.
   */
  static bool IsBytecode(const std::shared_ptr<const Buffer> &buffer);

  /**
   * Check if the given buffer is a valid bytecode buffer. Header version and
   * bytecode size are checked.
   * @param buffer code content
   * @return if the given buffer is a valid bytecode and an error message
   * if it's invalid.
   */
  static std::pair<bool, std::string> ValidateBytecode(
      const std::shared_ptr<const Buffer> &buffer);

  /**
   * Create a transformer class which can compile the given js source file to
   * a `QuickjsBytecodeProvider`.
   * @param source_url source url of the js source file.
   * @param src source code of the js file.
   * @return a `QuickjsBytecodeProviderSrc` representing the given js source
   * file.
   */
  static QuickjsBytecodeProviderSrc FromSource(
      std::string source_url, std::shared_ptr<const Buffer> src);

  /**
   * Create a `QuickjsBytecodeProvider` to unpack the given js bytecode.
   * @param bytecode a buffer representing the packed bytecode.
   * @return a `QuickjsBytecodeProvider` representing the packed bytecode, or an
   * error message if the buffer is invalid.
   */
  static base::expected<QuickjsBytecodeProvider, std::string>
  FromPackedBytecode(const std::shared_ptr<const Buffer> &bytecode);

  /**
   * Get the packed bytecode buffer.
   * @return the packed bytecode buffer.
   */
  std::shared_ptr<const Buffer> GetPackedBytecodeBuffer();

  /**
   * Get unpacked raw bytecode which can be executed by js engine.
   * @return the raw bytecode buffer.
   */
  std::shared_ptr<const Buffer> GetRawBytecode();

  /**
   * Get the target sdk version of the compiled raw bytecode.
   * @return the target sdk version of the compiled raw bytecode.
   */
  base::Version GetTargetSdkVersion();

  ~QuickjsBytecodeProvider() = default;

  friend class QuickjsBytecodeProviderSrc;

 private:
  Bytecode bytecode_;

  explicit QuickjsBytecodeProvider(Bytecode &&bytecode);
};
}  // namespace quickjs
}  // namespace piper
}  // namespace lynx

#endif  // CORE_RUNTIME_JSCACHE_QUICKJS_BYTECODE_QUICKJS_BYTECODE_PROVIDER_H_
